﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Xml;

namespace JOATServer.SocietyFramework.Core.XML
{
    public partial class XMLAutomaton : BaseXMLAutomaton
    {
        public enum EventType { StartElement, EndElement }

        public class Event
        {
            public EventType EventType { get; set; }
            public string TagName { get; set; }
            public Dictionary<string, string> Attributes { get; set; }

            public Event() { }

            public Event(EventType EventType, string TagName, Dictionary<string, string> attributes)
            {
                this.EventType = EventType;
                this.TagName = TagName;
                this.Attributes = attributes;
            }

        }

        protected class XMLAutomatonDeserializer
        {
            protected DeserializationAutomaton deserializationAutomaton;

            public XMLAutomatonDeserializer(DeserializationAutomaton automaton)
            {
                this.deserializationAutomaton = automaton;
            }

            public bool Deserialize(Stream inputStream)
            {
                try
                {
                    using (XmlReader reader = XmlReader.Create(inputStream))
                    {
                        while (reader.Read())
                        {
                            if(reader.Name == "xml") continue;

                            Event e = new Event();
                            e.TagName = reader.Name;
                            if (reader.IsStartElement())
                            {
                                e.EventType = EventType.StartElement;
                                e.Attributes = new Dictionary<string, string>();

                                if (reader.HasAttributes)
                                {
                                    while (reader.MoveToNextAttribute())
                                    {
                                        e.Attributes.Add(reader.Name, reader.Value);
                                    }
                                    reader.MoveToElement();
                                }

                                deserializationAutomaton.AddMessage(new Message(null, e));

                                if (reader.IsEmptyElement)
                                {
                                    e = new Event();
                                    e.TagName = reader.Name;
                                    e.EventType = EventType.EndElement;
                                    deserializationAutomaton.AddMessage(new Message(null, e));
                                }
                            }
                            else
                            {
                                e.EventType = EventType.EndElement;
                                deserializationAutomaton.AddMessage(new Message(null, e));
                                if (reader.Name == AUTOMATON_TAG)
                                {
                                    return true;
                                }
                            }
                        }
                    }
                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Automaton that takes the messages from the SAX Parser and creates the states, the transitions
        /// and the variables of the XMLAutomaton.<br/>
        /// Author: Micu Andrei
        /// </summary>
        public class DeserializationAutomaton : Automaton
        {
            protected static readonly string ENTRY_STATE = "ENTRY_STATE";
            protected static readonly string MERGE_IF_ELSE_STATE = "MERGE_IF_ELSE_STATE";
            protected static readonly string MERGE_SWITCH_CASE_STATE = "MERGE_SWITCH_CASE_STATE";
            protected static readonly string MERGE_SWITCH_DEFAULT_STATE = "MERGE_SWITCH_DEFAULT_STATE";
            protected static readonly string ATOMIC_INSTRUCTION_STATE = "ATOMIC_INSTRUCTION_STATE";
            protected static readonly string END_STATE = "END_STATE";
            protected static readonly string ERROR_STATE = "ERROR_STATE";

            public Stack<CodeContainer> containerStack;	//stack used to keep track of the code containers when adding instructions
            public Event currentEvent = null;			//reference to the event in the last message took from the message list
            public string errorMessage = null;

            public DeserializationAutomaton(XMLAutomaton automaton)
                : base()
            {
                containerStack = new Stack<CodeContainer>();
                currentStateName = ENTRY_STATE;

                states.Add(ENTRY_STATE, new NullState(this));
                states[ENTRY_STATE].transition = new EntryStateTransition(this);

                states.Add(AUTOMATON_TAG, new AutomatonState(this, automaton));
                states[AUTOMATON_TAG].transition = new AutomatonStateTransition(this);

                states.Add(CURRENT_MESSAGE_TAG, new CurrentMessageState(this, automaton));
                states[CURRENT_MESSAGE_TAG].transition = new CurrentMessageTransition(this);

                states.Add(MESSAGE_LIST_TAG, new NullState(this));
                states[MESSAGE_LIST_TAG].transition = new MessageListTransition(this);

                states.Add(MESSAGE_TAG, new MessageState(this, automaton));
                states[MESSAGE_TAG].transition = new MessageTransition(this);

                states.Add(VARIABLES_TAG, new NullState(this));
                states[VARIABLES_TAG].transition = new VariablesStateTransition(this);

                states.Add(VARIABLE_TAG, new DeserializationAutomaton.VariableState(this, automaton));
                states[VARIABLE_TAG].transition = new VariableStateTransition(this);

                states.Add(STATES_TAG, new NullState(this));
                states[STATES_TAG].transition = new StatesStateTransition(this);

                states.Add(STATE_TAG, new StateState(this, automaton));
                states[STATE_TAG].transition = new StateStateTransition(this);

                states.Add(TRANSITION_TAG, new TransitionState(this, automaton));
                states[TRANSITION_TAG].transition = new SimpleTransition(this, CODE_TAG);

                states.Add(TRANSITION_GROUP_TAG, new TransitionGroupState(this, automaton));
                states[TRANSITION_GROUP_TAG].transition = new TransitionGroupStateTransition(this);

                states.Add(CODE_TAG, new NullState(this));
                states[CODE_TAG].transition = new CodeStateTransition(this);

                states.Add(WHILE_TAG, new WhileState(this, automaton));
                states[WHILE_TAG].transition = new SimpleTransition(this, CODE_TAG);

                states.Add(DO_WHILE_TAG, new DoWhileState(this, automaton));
                states[DO_WHILE_TAG].transition = new SimpleTransition(this, CODE_TAG);

                states.Add(FOR_TAG, new ForState(this, automaton));
                states[FOR_TAG].transition = new SimpleTransition(this, CODE_TAG);

                states.Add(IF_TAG, new IfState(this, automaton));
                states[IF_TAG].transition = new SimpleTransition(this, CODE_TAG);

                states.Add(ELSE_TAG, new ElseState(this, automaton));
                states[ELSE_TAG].transition = new SimpleTransition(this, CODE_TAG);

                states.Add(MERGE_IF_ELSE_STATE, new MergeIfElseState(this, automaton));
                states[MERGE_IF_ELSE_STATE].transition = new SimpleTransition(this, CODE_TAG);

                states.Add(SWITCH_TAG, new SwitchState(this, automaton));
                states[SWITCH_TAG].transition = new SwitchStateTransition(this);

                states.Add(CASE_TAG, new CaseState(this, automaton));
                states[CASE_TAG].transition = new SimpleTransition(this, CODE_TAG);

                states.Add(DEFAULT_TAG, new DefaultState(this, automaton));
                states[DEFAULT_TAG].transition = new SimpleTransition(this, CODE_TAG);

                states.Add(MERGE_SWITCH_CASE_STATE, new MergeSwitchCaseState(this, automaton));
                states[MERGE_SWITCH_CASE_STATE].transition = new SimpleTransition(this, SWITCH_TAG);

                states.Add(MERGE_SWITCH_DEFAULT_STATE, new MergeSwitchDefaultState(this, automaton));
                states[MERGE_SWITCH_DEFAULT_STATE].transition = new SimpleTransition(this, SWITCH_TAG);

                states.Add(ATOMIC_INSTRUCTION_STATE, new AtomicInstructionState(this, automaton));
                states[ATOMIC_INSTRUCTION_STATE].transition = new AtomicInstructionStateTransition(this);

                states.Add(END_STATE, new EndState(this, automaton));
                states[END_STATE].transition = new SimpleTransition(this, END_STATE);

                states.Add(ERROR_STATE, new ErrorState(this, automaton));
                states[ERROR_STATE].transition = new SimpleTransition(this, ERROR_STATE);
            }


            /**** ENTRY STATE ****/
            protected class EntryStateTransition : Transition
            {
                public EntryStateTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.StartElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(AUTOMATON_TAG))
                        {   //begin the automaton construction
                            return AUTOMATON_TAG;
                        }
                    }
                    automaton.errorMessage = ENTRY_STATE;
                    return ERROR_STATE;
                }
            };

            /* AUTOMATON */
            protected class AutomatonState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public AutomatonState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    if (automaton.currentEvent.EventType == EventType.StartElement
                        && automaton.currentEvent.TagName.Equals(AUTOMATON_TAG))
                    {
                        ConstructedAutomaton.Name = automaton.currentEvent.Attributes[NAME_ATTRIBUTE];
                        ConstructedAutomaton.currentStateName = automaton.currentEvent.Attributes[CURRENT_STATE_ATTRIBUTE];
                        if(automaton.currentEvent.Attributes.ContainsKey(REGISTER_AT_START_ATTRIBUTE) &&
								automaton.currentEvent.Attributes[REGISTER_AT_START_ATTRIBUTE].Equals(".T.")){
                                    ConstructedAutomaton.RegisterAtStart = true;
						}
                    }
                }

            };

            protected class AutomatonStateTransition : Transition
            {
                public AutomatonStateTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.StartElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(CURRENT_MESSAGE_TAG))
                        {			//begin declaring the current message
							return CURRENT_MESSAGE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(MESSAGE_LIST_TAG))
                        {		    //begin declaring the message list
							return MESSAGE_LIST_TAG;
						}
                        else if (automaton.currentEvent.TagName.Equals(VARIABLES_TAG))
                        {			//begin declaring variables
                            return VARIABLES_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(STATES_TAG))
                        {		    //begin declaring states
                            return STATES_TAG;
                        }
                    }
                    else
                    {
                        if (automaton.currentEvent.TagName.Equals(AUTOMATON_TAG))
                        {			//end automaton construction
                            return END_STATE;
                        }
                    }
                    automaton.errorMessage = AUTOMATON_TAG;
                    return ERROR_STATE;
                }

            };

            /* CURRENT_MESSAGE */
            protected class CurrentMessageState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public CurrentMessageState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    //create the message and assign it to the currentMessage
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
					ConstructedAutomaton.currentMessage = 
                            new Message(automaton.currentEvent.Attributes[FROM_ATTRIBUTE],
										automaton.currentEvent.Attributes[DATA_ATTRIBUTE]);
                }

            };

            protected class CurrentMessageTransition : Transition
            {
                public CurrentMessageTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.EndElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(CURRENT_MESSAGE_TAG))
                        {			//end declaring the current message
                            return AUTOMATON_TAG;
                        }
                    }
                    automaton.errorMessage = CURRENT_MESSAGE_TAG;
                    return ERROR_STATE;
                }

            };

            /* MESSAGE LIST */
            protected class MessageListTransition : Transition
            {
                public MessageListTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.StartElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(MESSAGE_TAG))
                        {				//begin declaring a message
                            return MESSAGE_TAG;
                        }

                    }
                    else
                    {
                        if (automaton.currentEvent.TagName.Equals(MESSAGE_LIST_TAG))
                        {			//end declaring the message list
                            return AUTOMATON_TAG;
                        }
                    }
                    automaton.errorMessage = MESSAGE_LIST_TAG;
                    return ERROR_STATE;
                }

            };

            /* MESSAGE */
            protected class MessageState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public MessageState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    //create the message and add it to the list
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
					ConstructedAutomaton.messageList.Add(new
                            Message(automaton.currentEvent.Attributes[FROM_ATTRIBUTE],
                                    automaton.currentEvent.Attributes[DATA_ATTRIBUTE]));

                }

            };

            protected class MessageTransition : Transition
            {
                public MessageTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.EndElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(MESSAGE_TAG))
                        {			//end declaring the message
                            return MESSAGE_LIST_TAG;
                        }
                    }
                    automaton.errorMessage = MESSAGE_TAG;
                    return ERROR_STATE;
                }

            };

            /* VARIABLES */
            protected class VariablesStateTransition : Transition
            {
                public VariablesStateTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.StartElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(VARIABLE_TAG))
                        {				//begin declaring a variable
                            return VARIABLE_TAG;
                        }
                    }
                    else
                    {
                        if (automaton.currentEvent.TagName.Equals(VARIABLES_TAG))
                        {				//end declaring variables
                            return AUTOMATON_TAG;
                        }
                    }
                    automaton.errorMessage = VARIABLES_TAG;
                    return ERROR_STATE;
                }

            };

            /* VARIABLE */
            protected class VariableState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public VariableState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //add the variable to the Dictionary and set it's default value if it's specified
                    object defaultValue;
                    AssignInstruction ai = new AssignInstruction(ConstructedAutomaton);
                    ai.leftOperand = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[NAME_ATTRIBUTE]);
                    if ((defaultValue = automaton.currentEvent.Attributes[VALUE_ATTRIBUTE]) != null)
                    {
                        ai.rightOperand = ConstructedAutomaton.ConstructExpression((String)defaultValue);
                    }
                    else
                    {
                        ValueInstruction vi = new ValueInstruction(ConstructedAutomaton);
                        vi.value = 0;
                        ai.rightOperand = vi;
                    }
                    ai.Code();
                }

            };

            protected class VariableStateTransition : Transition
            {
                public VariableStateTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.EndElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(VARIABLE_TAG))
                        {				//end declaring a variable
                            return VARIABLES_TAG;
                        }
                    }
                    automaton.errorMessage = VARIABLE_TAG;
                    return ERROR_STATE;
                }

            };

            /* STATES */
            protected class StatesStateTransition : Transition
            {
                public StatesStateTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.StartElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(STATE_TAG))
                        {				//begin declaring a state
                            return STATE_TAG;
                        }
                    }
                    else
                    {
                        if (automaton.currentEvent.TagName.Equals(STATES_TAG))
                        {				//end declaring states
                            return AUTOMATON_TAG;
                        }
                    }
                    automaton.errorMessage = STATES_TAG;
                    return ERROR_STATE;
                }

            };

            /* STATE */
            protected class StateState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public StateState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    if (automaton.currentEvent.EventType == EventType.StartElement &&
                        automaton.currentEvent.TagName.Equals(STATE_TAG))
                    {
                        //add the state to the states list
                        XMLState newXMLState = new XMLState(ConstructedAutomaton);
                        ConstructedAutomaton.states.Add(automaton.currentEvent.Attributes[NAME_ATTRIBUTE], newXMLState);
                        automaton.containerStack.Push(newXMLState);
                    }
                }

            };

            protected class StateStateTransition : Transition
            {
                public StateStateTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.StartElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(CODE_TAG))
                        {					//begin declaring the code in the state
                            return CODE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(TRANSITION_TAG))
                        {			        //begin declaring a transition in the state
                            return TRANSITION_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(TRANSITION_GROUP_TAG))
                        {	                //begin declaring a transition group in the state
                            return TRANSITION_GROUP_TAG;
                        }
                    }
                    else
                    {
                        if (automaton.currentEvent.TagName.Equals(STATE_TAG))
                        {					//end declaring a state
                            automaton.containerStack.Pop();
                            return STATES_TAG;
                        }
                    }
                    automaton.errorMessage = STATE_TAG;
                    return ERROR_STATE;
                }

            };

            /* TRANSITION */
            protected class TransitionState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public TransitionState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    XMLTransition newXMLTransition = new XMLTransition(ConstructedAutomaton);
                    if (automaton.containerStack.Peek() is State)
                    {			//case when it's inside a state
                        ((State)automaton.containerStack.Peek()).transition = newXMLTransition;
                    }
                    else if (automaton.containerStack.Peek() is TransitionGroup)
                    {			//case when it's inside a transition group
                        ((TransitionGroup)automaton.containerStack.Peek()).transitions
                            .Add(automaton.currentEvent.Attributes[NAME_ATTRIBUTE], newXMLTransition);
                    }
                    automaton.containerStack.Push(newXMLTransition);
                }

            };

            /* TRANSITION GROUP */
            protected class TransitionGroupState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public TransitionGroupState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    if (automaton.currentEvent.EventType == EventType.StartElement &&
                        automaton.currentEvent.TagName.Equals(TRANSITION_GROUP_TAG))
                    {
                        XMLTransitionGroup newXMLTransitionGroup = new XMLTransitionGroup(ConstructedAutomaton);
                        if (automaton.containerStack.Peek() is State)
                        {		//case when it's inside a state
                            ((State)automaton.containerStack.Peek()).transition = newXMLTransitionGroup;
                        }
                        else if (automaton.containerStack.Peek() is TransitionGroup)
                        {		//case when it's inside a transition group
                            ((TransitionGroup)automaton.containerStack.Peek()).transitions
                            .Add(automaton.currentEvent.Attributes[NAME_ATTRIBUTE], newXMLTransitionGroup);
                        }
                        automaton.containerStack.Push(newXMLTransitionGroup);
                    }
                }

            };

            protected class TransitionGroupStateTransition : Transition
            {
                public TransitionGroupStateTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.StartElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(CODE_TAG))
                        {	//begin declaring the code in the transition group
                            return CODE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(TRANSITION_TAG))
                        {	//begin declaring a transition in the transition group
                            return TRANSITION_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(TRANSITION_GROUP_TAG))
                        {	//begin declaring a transition group in the transition group
                            return TRANSITION_GROUP_TAG;
                        }

                    }
                    else
                    {
                        if (automaton.currentEvent.TagName.Equals(TRANSITION_GROUP_TAG))
                        {			//end declaring a transition group
                            automaton.containerStack.Pop();
                            if (automaton.containerStack.Peek() is State)
                            {
                                return STATE_TAG;
                            }
                            else
                            {
                                return TRANSITION_GROUP_TAG;
                            }
                        }
                    }
                    automaton.errorMessage = TRANSITION_GROUP_TAG;
                    return ERROR_STATE;
                }

            };

            /* CODE STATE */
            protected class CodeStateTransition : Transition
            {
                public CodeStateTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType == EventType.StartElement)
                    {
                        if (automaton.currentEvent.TagName.Equals(WHILE_TAG))
                        {				//start declaring a while
                            return WHILE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(DO_WHILE_TAG))
                        {		        //start declaring a do-while
                            return DO_WHILE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(FOR_TAG))
                        {			    //start declaring a for
                            return FOR_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(IF_TAG))
                        {				//start declaring an if
                            return IF_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(ELSE_TAG))
                        {			    //start declaring an else
                            return ELSE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(SWITCH_TAG))
                        {			    //start declaring a switch
                            return SWITCH_TAG;
                        }
                        else
                        {				//if all the above fail, then the instruction may be an atomic instruction
                            return ATOMIC_INSTRUCTION_STATE;
                        }

                    }
                    else
                    {
                        if (automaton.currentEvent.TagName.Equals(CODE_TAG))
                        {	            //end declaring the code inside a state or transition group
                            if (automaton.containerStack.Peek() is State)
                            {
                                return STATE_TAG;
                            }
                            else if (automaton.containerStack.Peek() is TransitionGroup)
                            {
                                return TRANSITION_GROUP_TAG;
                            }
                        }
                        else if (automaton.currentEvent.TagName.Equals(TRANSITION_TAG))
                        {		        //end declaring a transition
                            automaton.containerStack.Pop();
                            if (automaton.containerStack.Peek() is State)
                            {
                                return STATE_TAG;
                            }
                            else if (automaton.containerStack.Peek() is TransitionGroup)
                            {
                                return TRANSITION_GROUP_TAG;
                            }
                        }
                        else if (automaton.currentEvent.TagName.Equals(WHILE_TAG))
                        {			    //end declaring a while
                            automaton.containerStack.Pop();
                            return CODE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(DO_WHILE_TAG))
                        {		        //end declaring a do-while
                            automaton.containerStack.Pop();
                            return CODE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(FOR_TAG))
                        {			    //end declaring a for
                            automaton.containerStack.Pop();
                            return CODE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(IF_TAG))
                        {				//end declaring an if
                            automaton.containerStack.Pop();
                            return CODE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(ELSE_TAG))
                        {			    //end declaring an else
                            automaton.containerStack.Pop();
                            return MERGE_IF_ELSE_STATE; //merge the last if and else
                        }
                        else if (automaton.currentEvent.TagName.Equals(CASE_TAG))
                        {			    //end declaring a case
                            automaton.containerStack.Pop();
                            return MERGE_SWITCH_CASE_STATE; //merge the last switch and case
                        }
                        else if (automaton.currentEvent.TagName.Equals(DEFAULT_TAG))
                        {		        //end declaring a default
                            automaton.containerStack.Pop();
                            return MERGE_SWITCH_DEFAULT_STATE; //merge the last switch and default
                        }
                    }
                    automaton.errorMessage = CODE_TAG;
                    return ERROR_STATE;
                }

            };

            /* WHILE */
            protected class WhileState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public WhileState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //add the while to the parent code container
                    WhileInstruction newWhile = new WhileInstruction(ConstructedAutomaton);
                    newWhile.testInstruction = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[TEST_ATTRIBUTE]);
                    automaton.containerStack.Peek().AddInstruction(newWhile);
                    automaton.containerStack.Push(newWhile);
                }

            };

            /* DO WHILE */
            protected class DoWhileState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public DoWhileState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //add the do-while to the parent code container
                    DoWhileInstruction newDoWhile = new DoWhileInstruction(ConstructedAutomaton);
                    newDoWhile.testInstruction = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[TEST_ATTRIBUTE]);
                    automaton.containerStack.Peek().AddInstruction(newDoWhile);
                    automaton.containerStack.Push(newDoWhile);
                }

            };

            /* FOR */
            protected class ForState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public ForState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //add the for to the parent code container
                    ForInstruction newFor = new ForInstruction(ConstructedAutomaton);
                    newFor.assignInstruction = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[ASSIGN_ATTRIBUTE]);
                    newFor.testInstruction = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[TEST_ATTRIBUTE]);
                    newFor.stepInstruction = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[STEP_ATTRIBUTE]);
                    automaton.containerStack.Peek().AddInstruction(newFor);
                    automaton.containerStack.Push(newFor);
                }

            };

            /* IF */
            protected class IfState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public IfState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //add the if to the parent code container
                    IfElseInstruction newIf = new IfElseInstruction(ConstructedAutomaton);
                    newIf.testInstruction = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[TEST_ATTRIBUTE]);
                    automaton.containerStack.Peek().AddInstruction(newIf);
                    automaton.containerStack.Push(newIf);
                }

            };

            /* ELSE */
            protected class ElseState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public ElseState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //add the else to the parent code container
                    IfElseInstruction newElse = new IfElseInstruction(ConstructedAutomaton);
                    automaton.containerStack.Peek().AddInstruction(newElse);
                    automaton.containerStack.Push(newElse);
                }

            };

            /* MERGE IF-ELSE */
            protected class MergeIfElseState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public MergeIfElseState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //merge the last if and else in the current instruction block
                    IfElseInstruction elseInstruction = (IfElseInstruction)automaton.containerStack.Peek().RemoveInstruction();
                    IfElseInstruction ifInstruction = (IfElseInstruction)automaton.containerStack.Peek().RemoveInstruction();
                    ifInstruction.failInstruction = elseInstruction.succeedInstruction;
                    automaton.containerStack.Peek().AddInstruction(ifInstruction);
                }

            };

            /* SWITCH */
            protected class SwitchState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public SwitchState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    if (automaton.currentEvent.EventType == EventType.StartElement &&
                        automaton.currentEvent.TagName.Equals(SWITCH_TAG))
                    {
                        //add a switch instruction in the current instruction block
                        SwitchInstruction newSwitchInstruction = new SwitchInstruction(ConstructedAutomaton);
                        newSwitchInstruction.argumentInstruction =
                            ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[VALUE_ATTRIBUTE]);
                        automaton.containerStack.Peek().AddInstruction(newSwitchInstruction);
                    }
                }

            };

            protected class SwitchStateTransition : Transition
            {
                public SwitchStateTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();


                    if (automaton.currentEvent.EventType.Equals(EventType.StartElement))
                    {
                        if (automaton.currentEvent.TagName.Equals(CASE_TAG))
                        {				//begin declaring a case
                            return CASE_TAG;
                        }
                        else if (automaton.currentEvent.TagName.Equals(DEFAULT_TAG))
                        {		        //begin declaring a default
                            return DEFAULT_TAG;
                        }
                    }
                    else
                    {
                        if (automaton.currentEvent.TagName.Equals(SWITCH_TAG))
                        {				//end declaring a switch
                            return CODE_TAG;
                        }
                    }
                    return null;
                }

            };

            /* CASE */
            protected class CaseState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public CaseState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //add a case instruction in the current instruction block
                    CaseInstruction newCaseInstruction = new CaseInstruction(ConstructedAutomaton);
                    newCaseInstruction.label = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[VALUE_ATTRIBUTE]);
                    automaton.containerStack.Peek().AddInstruction(newCaseInstruction);
                    automaton.containerStack.Push(newCaseInstruction);
                }

            };

            /* DEFAULT */
            protected class DefaultState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public DefaultState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //add a default instruction in the current instruction block
                    CaseInstruction newDefaultInstruction = new CaseInstruction(ConstructedAutomaton);
                    automaton.containerStack.Peek().AddInstruction(newDefaultInstruction);
                    automaton.containerStack.Push(newDefaultInstruction);
                }

            };

            /* MERGE SWITCH-CASE */
            protected class MergeSwitchCaseState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public MergeSwitchCaseState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //merge the last switch and case in the current instruction block
                    CaseInstruction caseInstruction = (CaseInstruction)automaton.containerStack.Peek().RemoveInstruction();
                    SwitchInstruction switchInstruction = (SwitchInstruction)automaton.containerStack.Peek().RemoveInstruction();
                    switchInstruction.caseInstructionsList.Add(caseInstruction);
                    automaton.containerStack.Peek().AddInstruction(switchInstruction);
                }

            };

            /* MERGE SWITCH-DEFAULT */
            protected class MergeSwitchDefaultState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public MergeSwitchDefaultState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    //merge the last switch and default in the current instruction block
                    CaseInstruction defaultInstruction = (CaseInstruction)automaton.containerStack.Peek().RemoveInstruction();
                    SwitchInstruction switchInstruction = (SwitchInstruction)automaton.containerStack.Peek().RemoveInstruction();
                    switchInstruction.defaultInstruction = defaultInstruction;
                    automaton.containerStack.Peek().AddInstruction(switchInstruction);
                }

            };

            /* ATOMIC INSTRUCTION */
            protected class AtomicInstructionState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public AtomicInstructionState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    Instruction atomicInstruction = null;
                    if (automaton.currentEvent.TagName.Equals(EXECUTE_TAG))
                    {						//construct an assign expression
                        ExpressionContainerInstruction eci = new ExpressionContainerInstruction();
                        eci.expression = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[EXPRESSION_ATTRIBUTE]);
                        atomicInstruction = eci;

                    }
                    else if (automaton.currentEvent.TagName.Equals(GET_NEXT_MESSAGE_TAG))
                    {		//construct a get_next_message
                        string timerString = (string) automaton.currentEvent.Attributes.Get(TIMER_ATTRIBUTE);
                        GetNextMessageInstruction gnmi = new GetNextMessageInstruction(ConstructedAutomaton);
                        if (timerString != null)
                        {
                            gnmi.timer = ConstructedAutomaton.ConstructExpression(timerString);
                        }
                        atomicInstruction = gnmi;

                    }
                    else if (automaton.currentEvent.TagName.Equals(SEND_MESSAGE_TAG))
                    {			//construct a send_message
                        SendMessageInstruction smi = new SendMessageInstruction(ConstructedAutomaton);
                        smi.to = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[TO_ATTRIBUTE]);
                        smi.data = ConstructedAutomaton.ConstructExpression(automaton.currentEvent.Attributes[DATA_ATTRIBUTE]);
                        atomicInstruction = smi;
                    }
                    else if (automaton.currentEvent.TagName.Equals(BREAK_TAG))
                    {					//construct a break instruction
                        ValueInstruction vi = new ValueInstruction(ConstructedAutomaton);
                        vi.value = SpecialSymbols.BREAK;
                        atomicInstruction = vi;
                    }
                    else if (automaton.currentEvent.TagName.Equals(BREAK_TAG))
                    {					//construct a continue instruction
                        ValueInstruction vi = new ValueInstruction(ConstructedAutomaton);
                        vi.value = SpecialSymbols.CONTINUE;
                        atomicInstruction = vi;
                    }
                    else if (automaton.currentEvent.TagName.Equals(RETURN_TAG))
                    {					//construct a return expression
                        object value = automaton.currentEvent.Attributes[VALUE_ATTRIBUTE];
                        if (value != null)
                        {										//returns a value
                            atomicInstruction = ConstructedAutomaton.ConstructExpression(value.ToString());
                        }
                        else
                        {										//returns null
                            atomicInstruction = new VariableInstruction(ConstructedAutomaton);
                        }
                    }

                    automaton.containerStack.Peek().AddInstruction(atomicInstruction);
                }
            };

            protected class AtomicInstructionStateTransition : Transition
            {
                public AtomicInstructionStateTransition(BaseAutomaton owner) : base(owner) { }

                public override string TransitionCode()
                {
                    DeserializationAutomaton automaton = ((DeserializationAutomaton)Owner);
                    automaton.GetNextEvent();

                    if (automaton.currentEvent.EventType.Equals(EventType.EndElement))
                    {
                        return CODE_TAG;
                    }
                    automaton.errorMessage = ATOMIC_INSTRUCTION_STATE;
                    return ERROR_STATE;
                }

            };

            /* END STATE */
            protected class EndState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public EndState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    Owner.StopSafely();
                    lock (Owner)
                    {
                        Monitor.Pulse(Owner);
                    }
                }

            };

            /* ERROR STATE */
            protected class ErrorState : State
            {
                protected XMLAutomaton ConstructedAutomaton { get; set; }

                public ErrorState(BaseAutomaton owner, XMLAutomaton constructedAutomaton)
                    : base(owner)
                {
                    this.ConstructedAutomaton = constructedAutomaton;
                }

                public override void StateCode()
                {
                    Owner.StopSafely();
                    lock (Owner)
                    {
                        Monitor.Pulse(Owner);
                    }
                }

            };


            /************************************ OTHER METHODS ************************************/

            public void WaitForFinish()
            {
                lock (this)
                {
                    if (this.currentStateName == null ||
                        !(this.currentStateName.Equals(END_STATE) || this.currentStateName.Equals(ERROR_STATE)))
                    {
                        Monitor.Wait(this);
                    }
                }
            }

            /// <summary>
            /// Gets the next message from the messageList and puts it in the currentMessage member, then
            /// puts the event from the message and puts it in the currentEvent member.
            /// </summary>
            protected void GetNextEvent()
            {
                GetNextMessage();
                currentEvent = (Event)currentMessage.Data;
            }
        }

    }
}
